﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Actya.Core.IndexDefinitions;
using Actya.Core.Infrastructure.Events;
using Actya.Core.SiteStructure.Events;
using Raven.Client;
using Raven.Client.Linq;

namespace Actya.Core.SiteStructure
{
	public class PageService : IPageService
	{
		private readonly IDocumentSession _session;
		private readonly IEventPublisher _eventPublisher;

		public PageService(IDocumentSession session, IEventPublisher eventPublisher)
		{
			_session = session;
			_eventPublisher = eventPublisher;
		}

		public Page FindPageByUrl(string url)
		{
			return _session.Query<Page>()
				.Customize(c => c.WaitForNonStaleResultsAsOfLastWrite())
				.SingleOrDefault(p => p.Url == url);
		}

		public IEnumerable<Page> GetMostRecentPages(int pageIndex, int pageSize, out int total)
		{
			var query = _session.Query<Page>()
				.Customize(c => c.WaitForNonStaleResultsAsOfLastWrite())
				.Where(p => p.IsPreviewPage == false)
				.OrderByDescending(p => p.DateModified);
			total = query.Count();
			return query.Skip(pageIndex*pageSize).Take(pageSize).ToArray();
		}

		public Page GetPageById(string id)
		{
			return _session.Load<Page>(id);
		}

		public void CreatePage(Page page, bool includeInNavigation, string parentNodeId, string menuText, bool isHomePage)
		{
            page.DateCreated = DateTime.Now;
            page.CreatedBy = Thread.CurrentPrincipal.Identity.Name;
            page.DateModified = DateTime.Now;
            page.ModifiedBy = Thread.CurrentPrincipal.Identity.Name;

            _session.Store(page);

			_eventPublisher.Publish(new PageCreated(page, includeInNavigation, parentNodeId, menuText, isHomePage));

			if (page.IsPublishedChanged && page.IsPublished)
			{
				_eventPublisher.Publish(new PagePublished(page));
			}
                
            _session.SaveChanges();
		}

		public void UpdatePage(Page page, bool includeInNavigation, string parentNodeId, string menuText, bool isHomePage)
		{
			page.DateModified = DateTime.Now;
			page.ModifiedBy = Thread.CurrentPrincipal.Identity.Name;

			_session.Store(page);

			_eventPublisher.Publish(new PageUpdated(page, includeInNavigation, parentNodeId, menuText, isHomePage));

			if (page.IsPublishedChanged)
			{
				if (page.IsPublished)
				{
					_eventPublisher.Publish(new PagePublished(page));
				}
				else
				{
					_eventPublisher.Publish(new PageUnPublished(page));
				}
			}

			_session.SaveChanges();
		}

		public void DeletePage(Page page)
		{
			_session.Delete(page);

			_eventPublisher.Publish(new PageDeleted(page));

			_session.SaveChanges();
		}

		public IEnumerable<Page> SearchPages(string term)
		{
			term = term + "*";
			return _session.Advanced.LuceneQuery<Page, Pages_BySearchTerm>()
				.Search("Title", term)
				.Search("Url", term)
				.Search("Content", term)
				.ToArray();
		}

		public IEnumerable<Page> FindPageByLayout(string layoutId)
		{
			return _session.Query<Page>().Where(p => p.Layout == layoutId).ToArray();
		}

		public void UnPublishPage(Page page)
		{
			page.Unpublish();
			_session.Store(page);
			_eventPublisher.Publish(new PageUnPublished(page));

			_session.SaveChanges();
		}

		public string[] GetPublishedAndAuthorizedPageIds(string[] allPageIds)
		{
			if (allPageIds.Length > 0)
			{
				var ids = _session.Query<Page>()
					.Where(p => p.Id.In(allPageIds)
					            && p.IsPublished
					            && (p.PublicationDate == null || p.PublicationDate <= DateTime.Now)
					            && (p.OfflineDate == null || p.OfflineDate >= DateTime.Now))
					.Select(p => new Page { Id = p.Id })
					.ToArray();
				return ids.Select(p => p.Id).ToArray();
			}
			return new string[0];
		}

		public void CreatePreviewPage(Page page)
		{
			page.DateCreated = DateTime.Now;
			page.CreatedBy = Thread.CurrentPrincipal.Identity.Name;
			page.DateModified = DateTime.Now;
			page.ModifiedBy = Thread.CurrentPrincipal.Identity.Name;
			page.IsPreviewPage = true;
			page.Url = Guid.NewGuid().ToString("N");

			_session.Store(page);
			_session.SaveChanges();
		}
	}
}
